/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.copier.engine;

import java.beans.*;
import ch.trackedbean.common.*;
import ch.trackedbean.copier.*;
import ch.trackedbean.util.*;

/**
 * Super class for {@link IMappingHolder}s which were using two normal properties.
 * 
 * @author M. Hautle
 */
public abstract class AbstractSimpleMappingHolder implements IMappingHolder {
	/** Object representing an unaccessable/undefined value. */
	protected static final Object UNDEFINED = new Object();

	/** The source value accessor. */
	protected final IValueAccessor srcProp;

	/** The destination accessor (on the object holding the mapping annotations). */
	protected final IValueAccessor dstProp;

	/** Flag indicating if the property should be fetched lazy. */
	protected final boolean lazy;

	/** Flag indicating if {@link #copyDst2Src(Object, Object, IMappingContext)} should be executed. */
	protected final boolean readOnly;

	/**
	 * Default constructor.
	 * 
	 * @param srcProp The source property
	 * @param dstProp The destination property (on the object holding the mapping annotations)
	 * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link #copyDst2Src(Object, Object, IMappingContext)} will not be executed
	 * @param lazy Flag indicating if the property should be fetched lazy
	 */
	protected AbstractSimpleMappingHolder(PropertyDescriptor srcProp, PropertyDescriptor dstProp, boolean readOnly, boolean lazy) {
		this(new PropertyAccessor(srcProp), new PropertyAccessor(dstProp), readOnly, lazy);
	}

	/**
	 * Default constructor.
	 * 
	 * @param el The expression
	 * @param targetType The type of the value reurned by the el expression
	 * @param dstProp The destination property (on the object holding the mapping annotations)
	 * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link #copyDst2Src(Object, Object, IMappingContext)} will not be executed
	 * @param lazy Flag indicating if the property should be fetched lazy
	 */
	@SuppressWarnings("unchecked")
	protected AbstractSimpleMappingHolder(String el, Class targetType, PropertyDescriptor dstProp, boolean readOnly, boolean lazy) {
		this(new ELAccessor(el, targetType), new PropertyAccessor(dstProp), readOnly, lazy);
	}

	/**
	 * Default constructor.
	 * 
	 * @param src The source accessor
	 * @param dst The destination accessor (on the object holding the mapping annotations)
	 * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link #copyDst2Src(Object, Object, IMappingContext)} will not be executed
	 * @param lazy Flag indicating if the property should be fetched lazy
	 */
	protected AbstractSimpleMappingHolder(IValueAccessor src, IValueAccessor dst, boolean readOnly, boolean lazy) {
		if (src == null)
			throw new IllegalArgumentException("The source accessor must be set!");
		if (dst == null)
			throw new IllegalArgumentException("The destination accessor must be set!");
		this.srcProp = src;
		this.dstProp = dst;
		this.lazy = lazy;
		this.readOnly = readOnly;
	}

	/**
	 * Returns if the mapped property should be fetched lazy.
	 * 
	 * @return True if this mapping should be fetched lazy
	 */
	public boolean isLazy() {
		return lazy;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final void copySrc2Dst(Object src, Object dst, IMappingContext context) throws TechnicalException {
		try {
			copyToDestination(src, dst, context);
		} catch (Exception e) {
			throw new TechnicalException("Error while copying!", e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final void copyDst2Src(Object src, Object dst, IMappingContext context) throws TechnicalException {
		if (!readOnly)
			try {
				copyToSource(src, dst, context);
			} catch (Exception e) {
				throw new TechnicalException("Error while copying!", e);
			}
	}

	/**
	 * Copies the value from the src object into the dst object.
	 * 
	 * @param dst The object holding the mapping
	 * @param src The other object
	 * @param context The mapping context
	 * @throws Exception If something goes wrong
	 */
	protected abstract void copyToDestination(Object src, Object dst, IMappingContext context) throws Exception;

	/**
	 * Copies the value from the dst object into the src object.
	 * 
	 * @param dst The object holding the mapping
	 * @param src The other object
	 * @param context The mapping context
	 * @throws Exception If something goes wrong
	 */
	protected abstract void copyToSource(Object src, Object dst, IMappingContext context) throws Exception;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IValueAccessor getSourceAccessor() {
		return srcProp;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IValueAccessor getDestinationAccessor() {
		return dstProp;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Object getDstValue(Object dst) {
		return dstProp.getValue(dst, null);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Object getSrcValue(Object src) {
		return srcProp.getValue(src, null);
	}
}
